package com.jp.kefu;

import org.apache.ibatis.builder.annotation.ProviderSqlSource;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author lijin
 */

@SuppressWarnings({"rawtypes", "unchecked"})
@Intercepts(
	   {
			 @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
	   }
)
public class ProviderInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
			 Object[] args = invocation.getArgs();
			 MappedStatement ms = (MappedStatement) args[0];
			 Object parameter = args[1];
			 RowBounds rowBounds = (RowBounds) args[2];
			 ResultHandler resultHandler = (ResultHandler) args[3];
			 Executor executor = (Executor) invocation.getTarget();
			 CacheKey cacheKey;
			 BoundSql boundSql;
			 //由于逻辑关系，只会进入一次
			 /*if(args.length == 4){
				//4 个参数时
				boundSql = getBoundSql(ms,parameter);
				cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
			 } else {
				//6 个参数时
				cacheKey = (CacheKey) args[4];
				boundSql = (BoundSql) args[5];
			 }

			 System.out.println(boundSql);*/

	   replaceSqlSource(ms);
			 return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
	   return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

	   System.out.println("======"+this.getClass()+".setProperties=======");
    }

    //替换方法占位符
    private void replaceSqlSource(MappedStatement ms) {


	   SqlSource sqlSource = ms.getSqlSource();
	   if (sqlSource instanceof ProviderSqlSource){
		  Configuration configuration = ms.getConfiguration();
		  ProviderSqlSource providerSqlSource = (ProviderSqlSource) sqlSource;
		  String msId = ms.getId();
		  int lastIndexOf = msId.lastIndexOf(".");

		  String mapperClassStr = msId.substring(0, lastIndexOf);
		  String mapperMethodStr = msId.substring(lastIndexOf+1);
		  //使用spring core工具设置私有属性
//		  DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(providerSqlSource);
		  MetaObject metaObject = SystemMetaObject.forObject(providerSqlSource);
		  MetaObject msObject = SystemMetaObject.forObject(ms);
		  Class providerType = (Class)metaObject.getValue("providerType");

		  try {


			 Class<?> modelType = Object.class;
			 Class<?> mapperClass = Class.forName(mapperClassStr);
			 Type[] types = mapperClass.getGenericInterfaces();
			 for (Type type : types) {
				if (type instanceof ParameterizedType) {
				    ParameterizedType t = (ParameterizedType) type;
				    if (t.getRawType() == mapperClass || mapperClass.isAssignableFrom((Class<?>) t.getRawType())) {
					   modelType = (Class<?>) t.getActualTypeArguments()[0];
					   break;
				    }
				}
			 }

			 EntityTable entityTable = EntityHelper.getEntityTable(modelType);
			 List<ResultMap> resultMaps = new ArrayList<ResultMap>();

			 MetaClass modelMetaClass = MetaClass.forClass(modelType, configuration.getReflectorFactory());


			 String[] setterNames = modelMetaClass.getSetterNames();

			 List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
			 for (String setterName : setterNames) {

				ResultMapping.Builder builder = new ResultMapping.Builder(configuration, setterName, setterName, modelMetaClass.getGetterType(setterName) );
				resultMappings.add(builder.build());
			 }


			 ResultMap.Builder builder = new ResultMap.Builder(configuration, "BaseMapperResultMap", modelType, resultMappings, true);
			 ResultMap resultMap = builder.build();
			 resultMaps.add(resultMap);
			 msObject.setValue("resultMaps", Collections.unmodifiableList(resultMaps));


			 Object instance = providerType.getConstructor(ResultMap.class).newInstance(resultMap);
			 Method providerTypeMethod = providerType.getMethod(mapperMethodStr, MappedStatement.class);
			 String xmlSql = (String)providerTypeMethod.invoke(instance);

			 LanguageDriver languageDriver = configuration.getLanguageRegistry().getDefaultDriver();
			 SqlSource newSqlSource = languageDriver.createSqlSource(ms.getConfiguration(), "<script>\n\t" + xmlSql + "</script>", null);

			 msObject.setValue("sqlSource", newSqlSource);

		  } catch (InstantiationException e) {
			 e.printStackTrace();
		  } catch (IllegalAccessException e) {
			 e.printStackTrace();
		  } catch (InvocationTargetException e) {
			 e.printStackTrace();
		  } catch (NoSuchMethodException e) {
			 e.printStackTrace();
		  } catch (ClassNotFoundException e) {
			 e.printStackTrace();
		  }
//			 MapperTemplate mapperTemplate = (MapperTemplate) providerType.getConstructor(Class.class, MapperHelper.class).newInstance(Class.forName(mapperClassStr), new MapperHelper());

	   }
    }
}
